﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Revit;
using YJKRevitRoom.Utility;

namespace YJKRevitRoom.NumberRoomControler
{
	public class VolumeNumberControler
	{
		public VolumeNumberControler(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
		}

		public VolumeNumberControler.ResultType CreateLabels(string roomTagFirstName, int roomTagNumber, string roomTagSecondName, int angle, int sortWay, ref string errMessage)
		{
			IList<Reference> list = null;
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
                Assist.RoomFilter roomFilter = new Assist.RoomFilter();
				list = selection.PickObjects((Autodesk.Revit.UI.Selection.ObjectType)1, roomFilter, "选择房间");
			}
			catch (Exception)
			{
				return VolumeNumberControler.ResultType.RT_Cancel;
			}
			if (list.Count == 0)
			{
				errMessage = "选择区域没有房间，请先创建房间后再进行编号。";
				return VolumeNumberControler.ResultType.RT_Exception;
			}
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			Transaction transaction = null;
			transaction = new Transaction(document);
			transaction.Start("add roomTag");
			Common.ChangeToTempNameWithRoom(document, list);
			int i;
			for (i = 0; i < list.Count; i++)
			{
				string text = roomTagFirstName + (roomTagNumber + i).ToString() + roomTagSecondName;
				if (Common.HasSameNameRoomTag(document, activeView.Id, text))
				{
					errMessage = "存在重复房间编号（" + text + ")";
					if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
					{
						transaction.RollBack();
					}
					return VolumeNumberControler.ResultType.RT_Exception;
				}
			}
			double num = (double)angle * Math.PI / 180.0;
			List<VolumeNumberControler.RoomPositionInfo> list2 = new List<VolumeNumberControler.RoomPositionInfo>();
			foreach (Reference reference in list)
			{
				Room room = this.m_Revit.Application.ActiveUIDocument.Document.GetElement(reference) as Room;
				if (room != null)
				{
					List<ElementId> tagsOfRoom = Common.GetTagsOfRoom(document, activeView.Id, room);
					if (tagsOfRoom.Count > 0)
					{
						foreach (ElementId elementId in tagsOfRoom)
						{
							document.Delete(elementId);
						}
					}
					Location location = room.Location;
					XYZ refPoint = this.GetRefPoint(room);
					XYZ transformPoint = this.GetTransformPoint(refPoint, -num, XYZ.BasisZ, (VolumeNumberControler.SortWay)sortWay);
					list2.Add(new VolumeNumberControler.RoomPositionInfo
					{
						position = transformPoint,
						room = room
					});
				}
			}
			list2.Sort((VolumeNumberControler.RoomPositionInfo left, VolumeNumberControler.RoomPositionInfo right) => XYComparer.Compare(left.position, right.position));
			i = 0;
			foreach (VolumeNumberControler.RoomPositionInfo roomPositionInfo in list2)
			{
				string tagName = roomTagFirstName + (roomTagNumber + i).ToString() + roomTagSecondName;
				XYZ point = (roomPositionInfo.room.Location as LocationPoint).Point;
				Common.CreateRoomTag(document, activeView, roomPositionInfo.room, point, tagName);
				i++;
			}
			transaction.Commit();
			return VolumeNumberControler.ResultType.RT_OK;
		}

		private XYZ GetRefPoint(Room room)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			SpatialElementBoundaryOptions spatialElementBoundaryOptions = new SpatialElementBoundaryOptions();
			IEnumerable<IList<BoundarySegment>> boundarySegments = room.GetBoundarySegments(spatialElementBoundaryOptions);
			List<XYZ> list = new List<XYZ>();
			foreach (IList<BoundarySegment> list2 in boundarySegments)
			{
				foreach (BoundarySegment segment in list2)
				{
					segment.GetElement(document);
					Curve curve = segment.GetCurve();
					list.Add(curve.GetEndPoint(0));
					list.Add(curve.GetEndPoint(1));
				}
			}
			list.Sort((XYZ left, XYZ right) => XYComparer.Compare(left, right));
			return list[0];
		}

		private XYZ TransformPoint(XYZ point, Transform transform)
		{
			double x = point.X;
			double y = point.Y;
			double z = point.Z;
			XYZ xyz = transform.get_Basis(0);
			XYZ xyz2 = transform.get_Basis(1);
			XYZ xyz3 = transform.get_Basis(2);
			XYZ origin = transform.Origin;
			double num = x * xyz.X + y * xyz2.X + z * xyz3.X + origin.X;
			double num2 = x * xyz.Y + y * xyz2.Y + z * xyz3.Y + origin.Y;
			double num3 = x * xyz.Z + y * xyz2.Z + z * xyz3.Z + origin.Z;
			return new XYZ(num, num2, num3);
		}

		private XYZ RotateTo(XYZ oldVector, double angle, XYZ axis)
		{
			Transform transform = RevitVersionFuncs.CreateRotationAtPoint(axis, angle, new XYZ(0.0, 0.0, 0.0));
			return this.TransformPoint(oldVector, transform);
		}

		private XYZ GetTransformPoint(XYZ oldVector, double angle, XYZ axis, VolumeNumberControler.SortWay sortWay)
		{
			XYZ xyz = this.RotateTo(oldVector, angle, axis);
			XYZ result = xyz;
			switch (sortWay)
			{
			case VolumeNumberControler.SortWay.SW_RightBottom:
				result = new XYZ(xyz.X, -xyz.Y, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_LeftTop:
				result = new XYZ(-xyz.X, xyz.Y, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_LeftBottom:
				result = new XYZ(-xyz.X, -xyz.Y, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_TopRight:
				result = new XYZ(xyz.Y, xyz.X, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_TopLeft:
				result = new XYZ(xyz.Y, -xyz.X, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_BottomRight:
				result = new XYZ(-xyz.Y, xyz.X, xyz.Z);
				break;
			case VolumeNumberControler.SortWay.SW_BottomLeft:
				result = new XYZ(-xyz.Y, -xyz.X, xyz.Z);
				break;
			}
			return result;
		}

		private ExternalCommandData m_Revit;

		private struct RoomPositionInfo
		{
			public XYZ position;

			public Room room;
		}

		private enum SortWay
		{
			SW_RightTop,
			SW_RightBottom,
			SW_LeftTop,
			SW_LeftBottom,
			SW_TopRight,
			SW_TopLeft,
			SW_BottomRight,
			SW_BottomLeft
		}

		public enum ResultType
		{
			RT_OK,
			RT_Cancel,
			RT_Exception
		}
	}
}
